// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef EIGEN_STDVECTOR_H
#define EIGEN_STDVECTOR_H

#include "details.h"

/**
 * This section contains a convenience MACRO which allows an easy specialization of
 * std::vector such that for data types with alignment issues the correct allocator
 * is used automatically.
 */
#define EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(...)                                                                                       \
    namespace std {                                                                                                                       \
    template <> class vector<__VA_ARGS__, std::allocator<__VA_ARGS__>> : public vector<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__>> \
    {                                                                                                                                     \
        typedef vector<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__>> vector_base;                                                    \
                                                                                                                                          \
    public:                                                                                                                               \
        typedef __VA_ARGS__ value_type;                                                                                                   \
        typedef vector_base::allocator_type allocator_type;                                                                               \
        typedef vector_base::size_type size_type;                                                                                         \
        typedef vector_base::iterator iterator;                                                                                           \
        explicit vector(const allocator_type& a = allocator_type()) : vector_base(a) {}                                                   \
        template <typename InputIterator>                                                                                                 \
        vector(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()) : vector_base(first, last, a)         \
        {                                                                                                                                 \
        }                                                                                                                                 \
        vector(const vector& c) : vector_base(c) {}                                                                                       \
        explicit vector(size_type num, const value_type& val = value_type()) : vector_base(num, val) {}                                   \
        vector(iterator start_, iterator end_) : vector_base(start_, end_) {}                                                             \
        vector& operator=(const vector& x)                                                                                                \
        {                                                                                                                                 \
            vector_base::operator=(x);                                                                                                    \
            return *this;                                                                                                                 \
        }                                                                                                                                 \
    };                                                                                                                                    \
    }

// Don't specialize if containers are implemented according to C++11
#if !EIGEN_HAS_CXX11_CONTAINERS

namespace std {

#define EIGEN_STD_VECTOR_SPECIALIZATION_BODY                                                                                  \
public:                                                                                                                       \
    typedef T value_type;                                                                                                     \
    typedef typename vector_base::allocator_type allocator_type;                                                              \
    typedef typename vector_base::size_type size_type;                                                                        \
    typedef typename vector_base::iterator iterator;                                                                          \
    typedef typename vector_base::const_iterator const_iterator;                                                              \
    explicit vector(const allocator_type& a = allocator_type()) : vector_base(a) {}                                           \
    template <typename InputIterator>                                                                                         \
    vector(InputIterator first, InputIterator last, const allocator_type& a = allocator_type()) : vector_base(first, last, a) \
    {                                                                                                                         \
    }                                                                                                                         \
    vector(const vector& c) : vector_base(c) {}                                                                               \
    explicit vector(size_type num, const value_type& val = value_type()) : vector_base(num, val) {}                           \
    vector(iterator start_, iterator end_) : vector_base(start_, end_) {}                                                     \
    vector& operator=(const vector& x)                                                                                        \
    {                                                                                                                         \
        vector_base::operator=(x);                                                                                            \
        return *this;                                                                                                         \
    }

template <typename T>
class vector<T, EIGEN_ALIGNED_ALLOCATOR<T>>
    : public vector<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T), Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)>>
{
    typedef vector<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T), Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)>> vector_base;
    EIGEN_STD_VECTOR_SPECIALIZATION_BODY

    void resize(size_type new_size) { resize(new_size, T()); }

#if defined(_VECTOR_)
    // workaround MSVC std::vector implementation
    void resize(size_type new_size, const value_type& x)
    {
        if (vector_base::size() < new_size)
            vector_base::_Insert_n(vector_base::end(), new_size - vector_base::size(), x);
        else if (new_size < vector_base::size())
            vector_base::erase(vector_base::begin() + new_size, vector_base::end());
    }
    void push_back(const value_type& x) { vector_base::push_back(x); }
    using vector_base::insert;
    iterator insert(const_iterator position, const value_type& x) { return vector_base::insert(position, x); }
    void insert(const_iterator position, size_type new_size, const value_type& x) { vector_base::insert(position, new_size, x); }
#elif defined(_GLIBCXX_VECTOR) && (!(EIGEN_GNUC_AT_LEAST(4, 1)))
    /* Note that before gcc-4.1 we already have: std::vector::resize(size_type,const T&).
   * However, this specialization is still needed to make the above EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION trick to work. */
    void resize(size_type new_size, const value_type& x) { vector_base::resize(new_size, x); }
#elif defined(_GLIBCXX_VECTOR) && EIGEN_GNUC_AT_LEAST(4, 2)
    // workaround GCC std::vector implementation
    void resize(size_type new_size, const value_type& x)
    {
        if (new_size < vector_base::size())
            vector_base::_M_erase_at_end(this->_M_impl._M_start + new_size);
        else
            vector_base::insert(vector_base::end(), new_size - vector_base::size(), x);
    }
#else
    // either GCC 4.1 or non-GCC
    // default implementation which should always work.
    void resize(size_type new_size, const value_type& x)
    {
        if (new_size < vector_base::size())
            vector_base::erase(vector_base::begin() + new_size, vector_base::end());
        else if (new_size > vector_base::size())
            vector_base::insert(vector_base::end(), new_size - vector_base::size(), x);
    }
#endif
};
}  // namespace std
#endif  // !EIGEN_HAS_CXX11_CONTAINERS

#endif  // EIGEN_STDVECTOR_H
